package ConnectWordsTool;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.HashMap;

import javax.swing.JButton;
import javax.swing.JPanel;

import project.V12;

/**
 * The panel that runs a exercise. Goes through every page and then returns to
 * ConnectWordsTool::displayScore with the finished exercise with scores.
 * @author Knut
 */
class RunExercisePanel extends JPanel {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1847425014203172714L;
	
	/**
	 * The parent instance of ConnectWordsTool.
	 */
	private ConnectWordsTool parent = null;
	
	/**
	 * The currently running exercise.
	 */
	private ConnectWordsExercise exercise;
	
	/**
	 * A counter that knows which page we are on.
	 */
	private int currentPageCounter = 0;
	
	/**
	 * The current page which the user is currently answering.
	 */
	private ConnectWordsPage currentPage;
	
	/**
	 * The words on left side (randomized)
	 */
	private String[] left;
	
	/**
	 * The words on the right side (randomized)
	 */
	private String[] right;
	
	/**
	 * The painting boundaries for the left-words. Indexes are equal to
	 * the words in RunExercisePanel::left.
	 * 
	 * Used for painting and mouse click detection.
	 */
	private Shape[] leftBounds;
	
	/**
	 * The painting boundaries for the right-words. Indexes are equal to
	 * the words in RunExercisePanel::right.
	 * 
	 * Used for painting and mouse click detection.
	 */
	private Shape[] rightBounds;
	
	/**
	 * The button which is labeled eighter "neste side" or "kontroller svarene".
	 */
	JButton button;
	
	/**
	 * Holds the connections that the user has connected.
	 * connection[leftIndex] = rightIndex.
	 * or if not connected:
	 * connection[leftIndex] = -1
	 */
	private int[] connections;
	
	/**
	 * Holds the validation info after the page is finished and validated:
	 * validations[leftIndex] = true/false
	 */
	private boolean[] validations = null;

	/**
	 * If we are currently dragging from a word on left side, this one
	 * gives the index in ConnectWordsTool::left or ConnectWordsTool::leftBounds
	 * for that word.
	 * -1 represent no dragging from a left side word.
	 */
	private int draggingFromLeft = -1;

	/**
	 * If we are currently dragging from a word on right side, this one
	 * gives the index in ConnectWordsTool::right or ConnectWordsTool::rightBounds
	 * for that word.
	 * -1 represent no dragging from a right side word.
	 */
	private int draggingFromRight = -1;
	
	/**
	 * If we are dragging a line, this represents the current location of the cursor.
	 * This one, in combination with draggingFromLeft or draggingFromRight gives
	 * paintComponent the information to draw a line when the user is dragging a line.
	 */
	private Point2D dragging = null;
	
	/**
	 * Builds the panel.
	 * @param parent
	 * @param exercise
	 * @throws Exception
	 */
	public RunExercisePanel(ConnectWordsTool parent, ConnectWordsExercise exercise) throws Exception {		
		this.parent = parent;
		this.exercise = exercise;
		
		MouseListener mouseListener = new MouseListener();
		this.addMouseListener(mouseListener);
		this.addMouseMotionListener(mouseListener);
		
		setSize(500, 500);
		
		if (exercise.numPages() == 0) {
			throw new Exception("No pages!");
		}
		
		loadPage(0);
		
		button = new JButton("Kontroller svarene");
		button.setEnabled(false);
		add(button);
		button.addActionListener(new ButtonListener());
		setLayout(null);
		button.setBounds(500, 500, 180, 30);
	}
	
	/**
	 * Load a new page into the view. Resets some variables and randomizes the words
	 * for the new page.
	 * @param page
	 */
	private void loadPage(int page) {
		currentPage = exercise.getPage(page);
		
		HashMap<String, String> words = currentPage.getWords();
		left = new String[words.size()];
		right = new String[words.size()];
		words.keySet().toArray(left);
		words.values().toArray(right);
		
		connections = new int[left.length];

		
		// randomize
		for (int i = 0; i < words.size(); i++) {
			int num = (int)(Math.random()*words.size());
			String temp = left[i];
			left[i] = left[num];
			left[num] = temp;
			
			num = (int)(Math.random() * words.size());
			temp = right[i];
			right[i] = right[num];
			right[num] = temp;
			
			connections[i] = -1;
		}
	}
	
	/**
	 * Paints the component.
	 */
	public void paintComponent(Graphics _g) {
		Graphics2D g = (Graphics2D)_g;
	
		// Initialize the bounds arrays:
		leftBounds = new Shape[left.length];
		rightBounds = new Shape[left.length];
		
		
		// Background color white:
		g.setColor(Color.decode("#DEFFA1"));
		g.fillRect(0, 0, getWidth(), getHeight());
		
		
		// The description of the page:
		g.setColor(Color.black);
		g.drawString(currentPage.getDescription(), 50, 50);
		
		// Set a font for the words:
		int fontSize = 25;
		g.setFont(new Font("Sans-serif", Font.BOLD, fontSize));
		
		// Print words:
		for (int i = 0; i < left.length; i++) {
			g.setColor(Color.black);
			
			Rectangle leftBoundaries = new Rectangle();
			leftBoundaries.setLocation(50, (int) (150+fontSize*1.8*i));
			leftBoundaries.setSize(200, fontSize);
			int width = g.getFontMetrics().stringWidth(left[i]);
			g.drawString(left[i], (int)leftBoundaries.getMaxX()-width, (int)leftBoundaries.getMinY()+fontSize);
			leftBounds[i] = leftBoundaries;
			
			Rectangle rightBoundaries = new Rectangle();
			rightBoundaries.setLocation(500, (int) (150+fontSize*1.8*i));
			rightBoundaries.setSize(200, fontSize);
			g.drawString(right[i], (int)rightBoundaries.getMinX(), (int)leftBoundaries.getMinY()+fontSize);
			rightBounds[i] = rightBoundaries;
		}
		
		// Draw connection lines:
		g.setColor(Color.blue);
		g.setStroke(new BasicStroke((float) 4.0));
		
		for (int i = 0; i < connections.length; i++) {
			if (connections[i] > -1) {

				if (validations != null) {
					if (validations[i]) {
						g.setColor(Color.green);
					} else {
						g.setColor(Color.red);
					}
				}
				
				Point2D left = new Point2D.Double(((Rectangle)leftBounds[i]).getMaxX()+5, ((Rectangle)leftBounds[i]).getCenterY());
				Point2D right = new Point2D.Double(((Rectangle)rightBounds[connections[i]]).getMinX()-5, ((Rectangle)rightBounds[connections[i]]).getCenterY());
				
				Line2D line = new Line2D.Double(left, right);
				g.draw(line);
				
			}			
		}
		
		// Draw currently dragging line:
		Point2D p0 = null;
		if (draggingFromLeft > -1) {
			p0 = new Point2D.Double(((Rectangle)leftBounds[draggingFromLeft]).getMaxX()+5, ((Rectangle)leftBounds[draggingFromLeft]).getCenterY());			
		} else if (draggingFromRight > -1) {
			p0 = new Point2D.Double(((Rectangle)rightBounds[draggingFromRight]).getMinX()-5, ((Rectangle)rightBounds[draggingFromRight]).getCenterY());
		}
		if (p0 != null) {
			Line2D line = new Line2D.Double(p0, dragging);
			g.draw(line);
		}
	}
	

	/**
	 * Connect two words.
	 * 
	 * @param left
	 * @param right
	 */
	public void connectWords(int left, int right) {
		
		for (int i = 0; i < connections.length; i++) {
			if (connections[i] == right) {
				connections[i] = -1;
			}
		}
		
		connections[left] = right;
		
		boolean disableButton = false;
		
		for (int i = 0; i < connections.length; i++) {
			if (connections[i] == -1) {
				disableButton = true;
			}
		}
		
		button.setEnabled(!disableButton);
		revalidate();
		repaint();
	}
	
	/**
	 * Listener for the button
	 * @author Knut
	 */
	private class ButtonListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			
			// If not validated: Validate
			if (validations == null) {
			
				HashMap<String, String> fasit = currentPage.getWords();
				validations = new boolean[connections.length];
				
				for (int i = 0; i < connections.length; i++) {
					
					if (fasit.get(left[i]).equals(right[connections[i]])) {
						validations[i] = true;
					} else {
						validations[i] = false;
					}
	
				}
				
				int score = 0;
				for (int i = 0; i < validations.length; i++) {
					if (validations[i]) {
						score++;
					}
				}
				
				exercise.scores.add(score);
				
				if (currentPageCounter < exercise.numPages() - 1) {
					button.setText("Neste side");
				} else {
					button.setText("Vis resultat");
				}
			
			// If validate: Display next page or scores:	
			} else {
				
				// If this was not the last page: display next page:
				if (currentPageCounter < exercise.numPages() - 1) {
					
					connections = null;
					validations = null;
					dragging = null;
					draggingFromLeft = -1;
					draggingFromRight = -1;
					left = null;
					right = null;
					leftBounds = null;
					rightBounds = null;
					
					button.setText("Kontroller svarene");
					button.setEnabled(false);
					
					loadPage(++currentPageCounter);
					
				// If this was the last page: go to scores:
				} else {
					
					parent.displayScore(exercise);
					
				}
			}
			V12.getInstance().update();
		}
	}
	
	// Mouse listener for line dragging:
	private class MouseListener extends MouseAdapter {		
		private int draggedFromLeft = -1;
		private int draggedFromRight = -1;
		
		/**
		 * Started dragging from a word?
		 */
		public void mouseDragged(MouseEvent e) {
			// No dragging after the page is validated:
			if (validations != null) {
				return;
			}
			
			// If not currently dragging:
			if (draggedFromLeft == -1 && draggedFromRight == -1) {
				for (int i = 0; i < left.length; i++) {
				
					if (leftBounds[i].contains(e.getPoint())) {
						// System.out.println("Started dragging from " + left[i]);
						connections[i] = -1;
						draggedFromLeft = i;
						draggingFromLeft = i;
					} else if (rightBounds[i].contains(e.getPoint())) {
						// System.out.println("Started dragging from " + right[i]);
						
						for (int j = 0; j < connections.length; j++) {
							if (connections[j] == i) {
								connections[j] = -1;
							}
						}
						
						draggedFromRight = i;
						draggingFromRight = i;
					}

				}
			}
			
			dragging = e.getPoint();			
			repaint();
		}
		
		
		/**
		 * Connects the words if the mouse is released after a dragging:
		 */
		public void mouseReleased(MouseEvent e) {
			// Don't make new connections after the page is validated:
			if (validations != null) {
				return;
			}
			
			if (draggedFromLeft > -1) {
				
				for (int i = 0; i < right.length; i++) {
					if (rightBounds[i].contains(e.getPoint())) {
						connectWords(draggedFromLeft, i);
						break;
					}
				}
				
			} else if (draggedFromRight > -1) {
				
				for (int i = 0; i < left.length; i++) {
					if (leftBounds[i].contains(e.getPoint())) {
						connectWords(i, draggedFromRight);
						break;
					}
				}
				
			}
			
			draggedFromLeft = draggedFromRight = -1;
			draggingFromLeft = draggingFromRight = -1;
			repaint();
		}
	}
}